#include <mips/regdef.h>
#include <sys/syscall.h>

#define RES_OK 0

	.text
	.align 2

	.global is_little_endian
	.ent is_little_endian
is_little_endian: # 3 SRA, 2 LTA
	.frame $fp, 20, ra
	.set noreorder
	.cpload t9
	.set reorder
	subu sp, sp, 20
	.cprestore 8
	sw $fp, 12(sp)
	move $fp, sp

# int n = 0xff
	li t1, 0xff
	sw t1, 0($fp)

# char* p = (char*)&n
	addu t1, $fp, zero
	sw t1, 4($fp)

# --if(p[0]) != 0)
	lw t1, 4($fp)
	lb t2, 0(t1)
	beqz t2, no_little_endian

# return 1
	li v0, 1
	j return_is_little_endian

no_little_endian:
# return 0
	li v0, 0

return_is_little_endian:
	lw $fp, 12(sp)
	addiu sp, sp, 20
	jr ra
	.end is_little_endian

	.global base64_to_ascii
	.ent base64_to_ascii
base64_to_ascii: # 3 SRA
	.frame $fp, 12, ra
	.set noreorder
	.cpload t9
	.set reorder
	subu sp, sp, 12
	.cprestore 0
	sw $fp, 4(sp)
	move $fp, sp
	sb a0, 12($fp)
	
# - if(c >= 'A' && c <= 'Z')
	lb t1, 12($fp)
	li t2, 65
	blt t1, t2, _base64_to_ascii_C2
	li t2, 90
	bgt t1, t2, _base64_to_ascii_C2
# - return c - 'A';
	lb v0, 12($fp)
	sub v0, v0, 65
	j return_base64_to_ascii
_base64_to_ascii_C2:
# - else if (c >= 'a' && c <= 'z')
	lb t1, 12($fp)
	li t2, 97
	blt t1, t2, _base64_to_ascii_C3
	li t2, 122
	bgt t1, t2, _base64_to_ascii_C3
# - return c - 'a' + 26;
	lb v0, 12($fp)
	sub v0, v0, 97
	add v0, v0, 26
	j return_base64_to_ascii
_base64_to_ascii_C3:
# - else if (c >= '0' && c <= '9')
	lb t1, 12($fp)
	li t2, 48
	blt t1, t2, _base64_to_ascii_C4
	li t2, 57
	bgt t1, t2, _base64_to_ascii_C4
# - return c - '0' + 52;
	lb v0, 12($fp)
	sub v0, v0, 48
	add v0, v0, 52
	j return_base64_to_ascii
_base64_to_ascii_C4:
# - else switch(c)
	lb t1, 12($fp)
# case '+':
	li t2, 43
	beq t1, t2, _base64_to_ascii_case1
# case '/':
	li t2, 47
	beq t1, t2, _base64_to_ascii_case2
# case '=':
	li t2, 61
	beq t1, t2, _base64_to_ascii_case3
# case default:
	j _base64_to_ascii_end_switch
_base64_to_ascii_case1:
# - return 62
	li v0, 62
	j return_base64_to_ascii
_base64_to_ascii_case2:
# - return 63
	li v0, 63
	j return_base64_to_ascii
_base64_to_ascii_case3:
# - return 0
	li v0, 0
	j return_base64_to_ascii
_base64_to_ascii_end_switch:
# - return -1
	li v0, -1
return_base64_to_ascii:
	lw $fp, 4(sp)
	addiu sp, sp, 12
	jr ra
	.end base64_to_ascii

	.global invert_block_on_little_endian
	.ent invert_block_on_little_endian
invert_block_on_little_endian: # 4 SRA, 1 LTA, 4 ABA
	.frame $fp, 36, ra
	.set noreorder
	.cpload t9
	.set reorder
	subu sp, sp, 36
	.cprestore 20
	sw $fp, 24(sp)
	move $fp, sp
	sw ra, 28($fp)
	sw a0, 36($fp)

# - char aux = block[3];
	lw t1, 36($fp)
	lb t2, 3(t1)
	sb t2, 16($fp)
# - if(!is_little_endian())
	jal is_little_endian
	bnez v0, invert_block_on_little_ endian_swap
# - return
	j return_invert_block_on_ little_endian
invert_block_on_ little_endian_swap:
# - block[3] = block[0];
	lw t1, 36($fp)
	lb t2, 0(t1)
	sb t2, 3(t1)
# - block[0] = aux;
	lw t1, 36($fp)
	lb t2, 16($fp)
	sb t2, 0(t1)
# - aux = block[2];
	lw t1, 36($fp)
	lb t2, 2(t1)
	sb t2, 16($fp)
# - block[2] = block[1];
	lw t1, 36($fp)
	lb t2, 1(t1)
	sb t2, 2(t1)
# - block[1] = aux;
	lw t1, 36($fp)
	lb t2, 16($fp)
	sb t2, 1(t1)
return_invert_block_on_ little_endian:
	lw ra, 28(sp)
	lw $fp, 24(sp)
	addiu sp, sp, 36
	jr ra
	.end invert_block_on_ little_endian

	.global _decode_block_main
	.ent _decode_block_main
_decode_block_main: # 3 SRA, 2 LTA
	.frame $fp, 20, ra
	.set noreorder
	.cpload t9
	.set reorder
	subu sp, sp, 20
	.cprestore 8
	sw $fp, 12(sp)
	move $fp, sp
	sw a0, 20($fp) # block
	sw a1, 24($fp) # decoded_block

# - unsigned int filter = 0xfc000000;
	li t1, 0xfc
	sll t1, t1, 24
	sw t1, 0($fp)
# - for (i = 1; i <= 4; i++)
# --- (i = 1)
	li t1, 1
	sw t1, 4($fp)
_decode_block_main_loop_start:
# --- (i <= 4)
	lw t1, 4($fp)
	li t2, 4
	bgt t1, t2, _decode_block_main_ loop_end
# - *decoded_block = *decoded_block | (filter & (*block << (2 * i)));
# --- t6 = (*block << (2 * i))
	lw t1, 4($fp)
	mul t1, t1, 2
	lw t2, 20($fp)
	lw t3, 0(t2)
	sllv t6, t3, t1
# --- t5 = filter & t6
	lw t1, 0($fp)
	and t5, t1, t6
# --- *decoded_block = *decoded_block | t5
	lw t1, 24($fp)
	lw t2, 0(t1)
	or t2, t2, t5
	sw t2, 0(t1)
# - filter = filter >> 6;
	lw t1, 0($fp)
	srl t1, t1, 6
	sw t1, 0($fp)
# --- (i++)
	lw t1, 4($fp)
	addi t1, t1, 1
	sw t1, 4($fp)
	j _decode_block_main_ loop_start
_decode_block_main_loop_end:
return__decode_block_main:
	lw $fp, 12(sp)
	addiu sp, sp, 20
	jr ra
	.end _decode_block_main

	.global _decode_block_body
	.ent _decode_block_body
_decode_block_body: # 4 SRA, 4 LTA, 4 ABA
	.frame $fp, 48, ra
	.set noreorder
	.cpload t9
	.set reorder
	subu sp, sp, 48
	.cprestore 32
	sw $fp, 36(sp)
	move $fp, sp
	sw ra, 40($fp)
	sw a0, 48($fp) # block
	sw a1, 52($fp) # decoded_block

# - unsigned int *_block, *_decoded_block; ---> 16($fp): _block, 20($fp): _decoded_block
# - char aux_block[4]; --> _decode_block_body_ aux_block --> 24($fp)
	la t1, _decode_block_body_ aux_block
	sw t1, 24($fp)
# - int i; ---> 28($fp)
# - memset(decoded_block, 0, 4);
	li t1, 0
	li t2, 4
	lw t3, 52($fp)
_decode_block_body_loop1:
	add t4, t2, t3
	sb t1, 0(t4)
	sub t2, t2, 1
	bnez t2, _decode_block_body_loop1
# - _block = (unsigned int*) aux_block;
	lw t1, 24($fp)
	sw t1, 16($fp)
# - _decoded_block = (unsigned int*) decoded_block;
	lw t1, 52($fp)
	sw t1, 20($fp)
# - for(i = 0; i < 4; i++)
# --- (i = 0)
	li t1, 0
	sw t1, 28($fp)
_decode_block_body_loop2_start:
# --- (i < 4)
	lw t1, 28($fp)
	li t2, 4
	bge t1, t2, _decode_block_body_ loop2_end
# - aux_block[i] = base64_to_ascii(block[i]);
	lw t1, 48($fp)
	lw t2, 28($fp)
	addu t3, t1, t2
	lb a0, 0(t3)
	jal base64_to_ascii
	lw t1, 24($fp)
	lw t2, 28($fp)
	add t3, t1, t2
	sb v0, 0(t3)
# --- (i++)
	lw t1, 28($fp)
	addi t1, t1, 1
	sw t1, 28($fp)
	j _decode_block_body_ loop2_start
_decode_block_body_loop2_end:
# - invert_block_on_little_ endian(aux_block);
	lw a0, 24($fp)
	jal invert_block_on_little_ endian
# - _decode_block_main(_block, _decoded_block);
	lw a0, 16($fp)
	lw a1, 20($fp)
	jal _decode_block_main
# - invert_block_on_little_ endian(decoded_block);
	lw a0, 52($fp)
	jal invert_block_on_ little_endian
return__decode_block_body:
	lw ra, 40(sp)
	lw $fp, 36(sp)
	addiu sp, sp, 48
	jr ra
	.end _decode_block_body

	.global decode_block
	.ent decode_block
decode_block: # 4 SRA, 2 LTA, 4 ABA
	.frame $fp, 40, ra
	.set noreorder
	.cpload t9
	.set reorder
	subu sp, sp, 40
	.cprestore 24
	sw $fp, 28(sp)
	move $fp, sp
	sw ra, 32($fp)
	sw a0, 40($fp) # block
	sw a1, 44($fp) # decoded_block

# - if (block[0] == '=')
	lw t1, 40($fp)
	lb t2, 0(t1)
	li t3, '='
	bne t2, t3, decode_block_C1
# - return -3;
	li v0, -3
	j return_decode_block
decode_block_C1:
# - if (block[1] == '=')
	lw t1, 40($fp)
	lb t2, 1(t1)
	li t3, '='
	bne t2, t3, decode_block_C2
# - return -2;
	li v0, -2
	j return_decode_block
decode_block_C2:
# - if ((block[2] == '=') && (block[3] != '='))
	lw t1, 40($fp)
	lb t2, 2(t1)
	li t3, '='
	bne t2, t3, decode_block_C3
	lw t1, 40($fp)
	lb t2, 3(t1)
	li t3, '='
	beq t2, t3, decode_block_C3
# - return -2;
	li v0, -2
	j return_decode_block
decode_block_C3:
# - _decode_block_body(block, decoded_block);
	lw a0, 40($fp)
	lw a1, 44($fp)
	jal _decode_block_body
# - if (block[2] == '=')
	lw t1, 40($fp)
	lb t2, 2(t1)
	li t3, '='
	bne t2, t3, decode_block_C4
# - return 1;
	li v0, 1
	j return_decode_block
decode_block_C4:
# - if (block[3] == '=')
	lw t1, 40($fp)
	lb t2, 3(t1)
	li t3, '='
	bne t2, t3, decode_block_C5
# - return 2;
	li v0, 2
	j return_decode_block
decode_block_C5:
	li v0, 3
return_decode_block:
	lw ra, 32(sp)
	lw $fp, 28(sp)
	addiu sp, sp, 40
	jr ra
	.end decode_block

	.global my_write
	.ent my_write
my_write: # 3 SRA
	.frame $fp, 12, ra
	.set noreorder
	.cpload t9
	.set reorder
	subu sp, sp, 12
	.cprestore 0
	sw $fp, 4(sp)
	move $fp, sp
	sw a0, 12($fp) # int outfd
	sw a1, 16($fp) # char* buffer
	sw a2, 20($fp) # size_t nbytes

# - return write(outfd, buffer, nbytes);
	li v0, SYS_write
	lw a0, 12($fp)
	lw a1, 16($fp)
	lw a2, 20($fp)
	syscall
	beqz a3, return_my_write
	li v0, -1
return_my_write:
	lw $fp, 4(sp)
	addiu sp, sp, 12
	jr ra
	.end my_write

	.global my_read
	.ent my_read
my_read: # 3 SRA
	.frame $fp, 12, ra
	.set noreorder
	.cpload t9
	.set reorder
	subu sp, sp, 12
	.cprestore 0
	sw $fp, 4(sp)
	move $fp, sp
	sw a0, 12($fp) # int outfd
	sw a1, 16($fp) # char* buffer
	sw a2, 20($fp) # size_t nbytes

# - return read(outfd, buffer, nbytes);
	li v0, SYS_read
	lw a0, 12($fp)
	lw a1, 16($fp)
	lw a2, 20($fp)
	syscall
	beqz a3, return_my_read
	li v0, -1
return_my_read:
	lw $fp, 4(sp)
	addiu sp, sp, 12
	jr ra
	.end my_read

	.data
	.align 2
_decode_block_body_aux_block:
	.space 4
